#include <iostream>

typedef void (*PROBLEM_FUNC) ();

struct problem_entry_t
{
	const char *id;		// id
	const char *title;	// display name
	PROBLEM_FUNC func;	// function pointer
};

///////////////////////////////////////////////////////////////////////////
// Google Code Jam problems

void gcj_2011_r0_a();
void gcj_2011_r0_b();
void gcj_2011_r0_c();

static const problem_entry_t gcj_problems[] = {
	{ "2011_r0_a", "Bot Trust", gcj_2011_r0_a },
	{ "2011_r0_b", "Magicka", gcj_2011_r0_b },
	{ "2011_r0_c", "Candy Splitting", gcj_2011_r0_c },
	{ NULL, NULL, NULL }
};

///////////////////////////////////////////////////////////////////////////
// Baidu Star problems

void astar_2011_r1a_1();
void astar_2011_r1b_1();
void astar_2011_r1b_2();

static const problem_entry_t astar_problems[] = {
	{ "2011_r1a_1", "Icon Arrangement", astar_2011_r1a_1 },
	{ "2011_r1b_1", "Numbers on a Ring", astar_2011_r1b_1 },
	{ "2011_r1b_2", "Random Number Recovery", astar_2011_r1b_2 },
	{ NULL, NULL, NULL }
};

///////////////////////////////////////////////////////////////////////////
// Program entry

struct problem_category_t
{
	const char *id;		// category label
	const char *title;	// display name
	const problem_entry_t* problems;
};

static const problem_category_t categories[] = {
	{ "gcj:", "Google Code Jam", gcj_problems },
	{ "astar:", "Baidu Star", astar_problems },
	{ NULL, NULL, NULL }
};

static void usage()
{
	std::cout << "usage: mathfun [-ltv] cat:id" << std::endl;
	std::cout << "where: cat:id is the problem id to solve." << std::endl;
	std::cout << "options" << std::endl;
	std::cout << "    -l    List available problems" << std::endl;
	std::cout << "    -t    Time the execution of the program" << std::endl;
	std::cout << "    -v    Verbose mode" << std::endl;
}

static PROBLEM_FUNC find_problem(const char *id)
{
	// Find category
	const problem_entry_t *entry = NULL;
	for (const problem_category_t *cat = categories; cat->id; ++cat)
	{
		if (strncmp(cat->id, id, strlen(cat->id)) == 0)
		{
			entry = cat->problems;
			id += strlen(cat->id);
			break;
		}
	}
	if (!entry)
		return NULL;

	// Find problem within category
	for (; entry->id; ++entry)
	{
		if (strcmp(entry->id, id) == 0)
			return entry->func;
	}
	return NULL;
}

static void list_problems()
{
}

int main(int argc, char *argv[])
{
	bool verbose = false;
	bool timing = false;
	PROBLEM_FUNC f = NULL;

	// Parse command line arguments.
	for (int i = 1; i < argc; i++)
	{
		const char *s = argv[i];
		if (s[0] == '-')
		{
			for (int j = 1; s[j] != '\0'; j++)
			{
				switch (s[j])
				{
				case 'v': 
					verbose = true; 
					break;
				case 't':
					timing = true;
					break;
				case 'l':
					list_problems();
					break;
				default:
					std::cerr << "Unknown option: -" << s[j] << std::endl;
					return 2;
				}
			}
		}
		else
		{
			f = find_problem(s);
			if (!f)
			{
				std::cerr << "Cannot find problem: " << s << std::endl;
				return 3;
			}
		}
	}

	if (!f)
	{
		usage();
		return 2;
	}

	// Run the problem and time it.
#if 0
	hr_timer t;
	t.start();
#endif
	//int cases = 0;
	//std::cin >> cases;
	//for (int i = 1; i <= cases; i++)
	//{
	//	std::cout << "Case #" << i << ": ";
		f();
	//}
#if 0
	if (timing)
	{
		double t1 = t.stop();
		std::cerr.precision(4);
		std::cerr << std::fixed << t1 << std::endl;
	}
#endif

	//system("PAUSE");
	return 0;
}
